/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package SPARQL;

import Rewriting.SetUtils;
import Rewriting.VariableNameGenerator;
import java.util.*;
import java.util.Map.Entry;

/**
 *
 * @author riccardo
 */
public class SPARQL {
    private LinkedList<Term> selectVariables;
    private HashSet<TriplePattern> triplePatterns;
    private HashMap<String,SimpleURI> prefixes;
    private boolean distinct;
    private int limit;
    private String filter;
    private Set<Service> services;
    private List<Binding> bindings;
    
    public SPARQL(LinkedList<Term> selectVariables, HashSet<TriplePattern> triplePatterns, HashMap<String, SimpleURI> prefixes, boolean distinct, int limit, String filter) {
        this.selectVariables = selectVariables;
        this.triplePatterns = triplePatterns;
        this.prefixes = prefixes;
        this.distinct = distinct;
        this.limit = limit;
        this.filter=filter;
        this.services= new HashSet<Service>();
        this.bindings = new ArrayList<Binding>();
    }

    public List<Binding> getBindings() {
        return bindings;
    }

    public void setBindings(List<Binding> bindings) {
        this.bindings = bindings;
    }
    
    public boolean addBindings(Binding binding) {
        return this.bindings.add(binding);
    }
    
    
    
    public void addSerivice(Service service){
        this.services.add(service);
    }

    public Set<Service> getServices() {
        return services;
    }

    public void setServices(Set<Service> services) {
        this.services = services;
    }

    public LinkedList<Term> getSelectVariables() {
        return selectVariables;
    }

    public HashSet<TriplePattern> getTriplePatterns() {
        return triplePatterns;
    }

    public HashMap<String,SimpleURI> getPrefixes() {
        return prefixes;
    }

    public boolean isDistinct() {
        return distinct;
    }

    public int getLimit() {
        return limit;
    }
    
    /*
    @Override
    public int hashCode() {
        int hash = 7;
        hash = 89 * hash + Objects.hashCode(this.selectVariables);
        hash = 89 * hash + Objects.hashCode(this.triplePatterns);
        //hash = 89 * hash + Objects.hashCode(this.prefixes);
        //hash = 89 * hash + (this.distinct ? 1 : 0);
        //hash = 89 * hash + this.limit;
        return hash;
    }
    */

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 41 * hash + Objects.hashCode(this.selectVariables);
        hash = 41 * hash + Objects.hashCode(this.triplePatterns);
        hash = 41 * hash + Objects.hashCode(this.services);
        hash = 41 * hash + Objects.hashCode(this.bindings);
        return hash;
    }

    
    
    


    @Override
    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        SPARQL other = (SPARQL) obj;
        if (!Objects.equals(this.selectVariables, other.selectVariables)) {
            //System.out.println("false equals select variables"  +this + other);
        return false;
        }
        if (!Objects.equals(this.triplePatterns, other.triplePatterns)){
           
            return false;
        }
        
        if (!Objects.equals(this.bindings, other.bindings)) {
            
            return false;
        }
        
        if (!Objects.equals(this.services, other.services)) {
            
            return false;
        }
       //if (!Objects.equals(this.prefixes, other.prefixes)) {
           // return false;
        
        //}
        //if (this.distinct != other.distinct) {
           // return false;
       // }
       //if (this.limit != other.limit) {
            //return false;
        //}
        return true;
    }

    
    @Override
    public String toString() {
        String ret = "";
        if(prefixes!=null)for(Entry<String,SimpleURI> p:prefixes.entrySet())ret=ret+"PREFIX "+p.getKey()+":"+p.getValue()+"\n";
        if(!selectVariables.isEmpty()){
            ret=ret+"SELECT ";
            if(distinct)ret=ret+"DISTINCT ";
            for(Term v: selectVariables){
                ret+=v.toString()+" ";
            }
            ret+=" WHERE { ";
        } 
        else ret+= " ASK{ ";
        boolean serviceTest = false;
        Set<TriplePattern> triplesInServices = new HashSet<TriplePattern>();
        int servicecounter = 0;
        for (Service service: this.services){
            
            
            if (service.getTriplePatterns().size()>0){
                serviceTest = true;
                if (servicecounter>0) ret+=" . ";
                ret+="SERVICE "+service.getUri()+" {";
                servicecounter++;

                int bodycounter =1;
                for(TriplePattern t: service.getTriplePatterns()){
                    ret+=t;
                    if (bodycounter<service.getTriplePatterns().size()) ret+=" . ";
                    bodycounter++;
                    triplesInServices.add(t);
                }
                ret+=" } ";
            }
        }
        boolean firstIteration = true;
        
        for(TriplePattern t: triplePatterns){
            if(!triplesInServices.contains(t)){
                if (firstIteration&&serviceTest) ret+=" . ";
                ret+=t+" . ";
                firstIteration = false;
            }
        }
        
        
        if(!firstIteration) ret=ret.substring(0, ret.length()-2);
        if (this.bindings.size()>0) {
            for (Binding binding: this.bindings) {
                ret+=" "+binding+" ";
            }
        }
            if(filter!=null)ret=ret+filter;
            ret=ret+"}";
        
        if(limit>=1) ret=ret+" LIMIT "+limit;
        return ret;
    }
    
    public String toStringNoAsk() {
        String ret = "";
        if(prefixes!=null)for(Entry<String,SimpleURI> p:prefixes.entrySet())ret=ret+"PREFIX "+p.getKey()+":"+p.getValue()+"\n";
        if(!selectVariables.isEmpty()){
            ret=ret+"SELECT ";
            if(distinct)ret=ret+"DISTINCT ";
            for(Term v: selectVariables){
                ret+=v.toString()+" ";
            }
            ret+=" WHERE{ ";
        } 
        else ret+= " { ";
        for(TriplePattern t: triplePatterns){
            ret+=t+" . ";
        }
        ret=ret.substring(0, ret.length()-2);
        if(filter!=null)ret=ret+filter;
        ret=ret+"}";
        if(limit>=1) ret=ret+" LIMIT "+limit;
        return ret;
    }

    @Override
    public Object clone(){
        LinkedList<Term> vars = new LinkedList<Term>();
        for(Term v: selectVariables)vars.add((Term)v.clone());
        HashSet<TriplePattern> tps = new HashSet<TriplePattern>();
        for(TriplePattern t: triplePatterns)tps.add((TriplePattern)t.clone());
        HashMap<String,SimpleURI> ps = new HashMap<String,SimpleURI>();
        if (this.prefixes!=null)
        for(Entry<String,SimpleURI> p : prefixes.entrySet()) ps.put(p.getKey(),p.getValue());
        SPARQL newQuery = new SPARQL(vars, tps, ps,distinct,limit,filter);
         
        if (this.services!= null)
            for (Service service : this.services) {
                newQuery.addSerivice((Service) service.clone());
            }
        if (this.bindings!=null)
            for(Binding bind : this.bindings)
                newQuery.addBindings((Binding) bind.clone());
        
        return newQuery;
    }
    
    public Set<Variable> getVaribles(){
        
        TriplePattern triple;
        Iterator<TriplePattern> iterator = this.triplePatterns.iterator();
        Set<Variable> variables = new HashSet<Variable>();
        while (iterator.hasNext()){
            triple=iterator.next();
            if (triple.getS() instanceof Variable)
                variables.add((Variable) triple.getS());
            if (triple.getP() instanceof Variable)
                variables.add((Variable) triple.getP());
            if (triple.getO() instanceof Variable)
                variables.add((Variable) triple.getO());
        }
            return variables;
    }

    private void printbug(HashSet<TriplePattern> triplePatterns, HashSet<TriplePattern> triplePatterns0) {
        
        System.out.println("triple patterns are equals??? " + triplePatterns.equals(triplePatterns0));
        System.out.println("bug");
        System.out.println("sizes " + triplePatterns.size()+ triplePatterns0.size());
        for (TriplePattern triple1 : triplePatterns){
        for (TriplePattern triple2 : triplePatterns0){
            System.out.println(" contains : ? "+ triplePatterns.contains(triple2));
            System.out.println(triple1 + " " + triple2 + " " + triple1.hashCode()+ " "+ triple2.hashCode() +" "+ triple1.equals(triple2));
            
        }
        
        
            
        }
        
        
        }
    
    public void removeIRIinSelect(){
        VariableNameGenerator varGenerator = VariableNameGenerator.getInstance();
        for (int i=0; i<this.selectVariables.size(); i++ ){
            Term term = this.selectVariables.get(i);
            if (term instanceof URI){
                Variable variable = new Variable(varGenerator.newVariable());
                this.addBindings(new Binding(term,variable));
                this.selectVariables.set(i, variable);
            }
        }
    }
    
    public Set<Variable> getSetOfAllVariables() {
            Set<Variable> variables = new HashSet<Variable>();
            for (Term term : this.getSelectVariables())
                if (term instanceof Variable)
                    variables.add((Variable) term);
            
            for (Variable variable : this.getVaribles())
                variables.add(variable);
            return variables;
    }
    
    public int sizeOfVariables (){
    return this.getSetOfAllVariables().size();
            }

    public void addSerivice(SimpleURI endpoint, TriplePattern triple) {
        
        boolean test = false;
        outerloop:
        for (Service service : this.services)
            if (endpoint.equals(service.getUri())){
                
                service.addTriple(triple);
                test = true;
                break outerloop;
            }
        if (!test) {
            Service service = new Service(endpoint);
            service.addTriple(triple);
            this.services.add(service);
            
            
        }
            
    }
}
